import View from './components/view'
import Link from './components/link'

// 保存 Vue 的局部变量，并导出，使 vue-router 在任何地方都能访问到 Vue
export let _Vue

export function install(Vue) {
  // 如果是多次注册，就会 return 不会进行重复的注册
  if (install.installed && _Vue === Vue) return
  // install.installed = true 代表已经注册过
  install.installed = true

  // 因为在上面通过 export let _Vue 将 Vue 导出，使 vue-router 在任何时候都能访问到 Vue
  // 好处: 在其它模块中，可以导入这个 _Vue，这样既能访问到 Vue，又避免了将 Vue 做为依赖打包
  _Vue = Vue

  const isDef = (v) => v !== undefined

  const registerInstance = (vm, callVal) => {
    let i = vm.$options._parentVnode
    if (
      isDef(i) &&
      isDef((i = i.data)) &&
      isDef((i = i.registerRouteInstance))
    ) {
      i(vm, callVal)
    }
  }

  // 通过 Vue.mixin 去做全局混入
  // 当组件实例化执行到 beforeCreate、destroyed 钩子时都会执行这里定义的逻辑
  Vue.mixin({
    beforeCreate() {
      // 判断是否在 new Vue 的时候是否把 router 传入
      // 传进来了，会在 Vue.$options 上挂载有 router
      // new Vue({ el: 'app', router })
      if (isDef(this.$options.router)) {
        // 将 Vue 赋值给 this._routerRoot  this 是new Vue（） 跟组件 实例
        this._routerRoot = this
        // 将传入的 router 赋值给 this._router
        // 传入的 router 是通过 new VueRouter({mode: '', routes: [{}]}) 出来的
        this._router = this.$options.router
        // VueRouter 类身上有 init 方法，主要是进行 VueRouter 的初始化
        // 将 this 当做参数，this 是 vue
        this._router.init(this) // 这里只有根组件才执行
        // 将 _route 变成响应式的，后续 route 变化，重新渲染
        // 加上数据劫持  _route 变了 触发 router-view 的重新渲染
        Vue.util.defineReactive(this, '_route', this._router.history.current)
      } else {
        // 子组件
        // 子组件的 this._routerRoot 访问到 组件的实例 然后下面代理了 子组件就可以访问懂啊 _router了
        // Object.defineProperty(Vue.prototype, '$route', {
        //   get() {
        //     return this._routerRoot._route
        //   },
        // })
        this._routerRoot = (this.$parent && this.$parent._routerRoot) || this
      }
      registerInstance(this, this)
    },
    destroyed() {
      registerInstance(this)
    },
  })

  // 通过 Object.defineProperty 代理的方式往 Vue 原型上加入 $router 实例
  // 这样在使用的时候可以通过 this.$router 访问
  // '$router 访问的是 new VueRouter（）实例
  Object.defineProperty(Vue.prototype, '$router', {
    get() {
      return this._routerRoot._router
    },
  })

  // 通过 Object.defineProperty 代理的方式往 Vue 原型上加入 $route
  // 这样在使用的时候可以通过 this.$route 访问
  // $route 访问的是 $router.history.current 对象
  Object.defineProperty(Vue.prototype, '$route', {
    get() {
      return this._routerRoot._route
    },
  })

  // 全局注册 router-view 和 router-link 这两个组件
  Vue.component('RouterView', View)
  Vue.component('RouterLink', Link)

  // 设置路由组件的 beforeRouteEnter、beforeRouteLeave、beforeRouteUpdate 守卫的合并策略
  const strats = Vue.config.optionMergeStrategies
  // use the same hook merging strategy for route hooks
  strats.beforeRouteEnter =
    strats.beforeRouteLeave =
    strats.beforeRouteUpdate =
      strats.created
}

// $router 对象：
// $router 是 VueRouter 的实例。
// 它包含了整个路由的配置信息和方法，用于编程式导航。
// 常用的方法有：
// push(location)：导航到一个新的 URL。
// replace(location)：用一个新的 URL 替换当前的历史记录。
// go(n)：前进或后退 n 个历史记录。
// 你可以通过 $router 来访问路由实例的全局方法，比如添加路由守卫等。

// $route 对象：
// $route 是一个当前激活的路由对象，包含了当前 URL 的信息。
// 它包含了当前路由的路径、查询参数、嵌套路由对象等信息。
// 常用的属性有：
// path：当前路由的路径。
// query：一个包含查询参数的对象。
// params：一个包含动态段值的对象。
// name：当前路由的名称（如果定义了的话）。
// component：当前路由对应的组件。
// $route 对象通常用于在组件内部获取当前路由的信息。
